/*

 * Copyright (C) 2021 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */
package com.iwgang.familiarrecyclerview;

import ohos.agp.components.*;
import ohos.app.Context;
import ohos.utils.PlainArray;

import java.util.ArrayList;
import java.util.List;

/**
 * 网格布局适配器
 *
 * @param <T> T
 */
public abstract class GridViewAdapter<T> extends BaseItemProvider {
    private final Context mContext;
    private final List<T> mData;
    private final int     mLayoutId;
    private       int     mNumColumns = 1;

    // 计算得到的行数
    private int mLineInt;

    // 一行多列，不能整除时，最后一行的列数
    private       int                  mColumnYu;
    private final ArrayList<Component> mHeaderViews = new ArrayList<>();
    private final ArrayList<Component> mFooterViews = new ArrayList<>();
    private final int                  HEAD_TYPE    = 11111;
    private final int                  FOOT_TYPE    = 22222;
    private final int                  NORMAL_TYPE  = 0;
    private       Component            mEmptyView;
    private       boolean              mIsAdd       = false;

    public GridViewAdapter(Context context, List<T> data, int layoutId) {
        this.mContext = context;
        this.mData = data;
        this.mLayoutId = layoutId;
    }

    @Override
    public int getCount() {
        mColumnYu = mData.size() % mNumColumns;
        if (mColumnYu > 0) {
            mLineInt = mData.size() / mNumColumns + 1;
        } else {
            mLineInt = mData.size() / mNumColumns;
        }
        if (mLineInt > 0) {
            return mLineInt + getHeaderViewsCount() + getFooterViewsCount();
        } else {
            if (getEmptyView() != null) {
                return getHeaderViewsCount() + getFooterViewsCount() + 1;
            } else {
                return getHeaderViewsCount() + getFooterViewsCount();
            }
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public T getItem(int position) {
        if (getItemComponentType(position) == HEAD_TYPE) {
            return (T) getHeaderView();
        } else if (getItemComponentType(position) == FOOT_TYPE) {
            return (T) getFooterView();
        } else {
            return mData.get(position - getHeaderViewsCount());
        }
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public int getItemComponentType(int position) {
        if ((getHeaderViewsCount() > 0) && (position < getHeaderViewsCount())) {
            return HEAD_TYPE;
        } else if ((getFooterViewsCount() > 0) && (position <= (getCount() - 1) &&
                (position > (getCount() - getFooterViewsCount() - 1)))) {
            return FOOT_TYPE;
        } else {
            return NORMAL_TYPE;
        }
    }

    /**
     * refresh
     *
     * @param isAdd isAdd
     */
    public void refresh(boolean isAdd) {
        mIsAdd = isAdd;
        this.notifyDataChanged();
    }

    @Override
    public Component getComponent(int position, Component component, ComponentContainer componentContainer) {
        if (getItemComponentType(position) == HEAD_TYPE) {
            return getHeaderView();
        } else if (getItemComponentType(position) == FOOT_TYPE) {
            return getFooterView();
        } else {
            if (mLineInt <= 0) {
                return getEmptyView();
            }
            GridViewHolder viewHolder = GridViewHolder.get(mContext,
                    component, componentContainer, mLayoutId, mIsAdd);
            List<T> models = new ArrayList<T>();
            int[] positions;
            // 可以被整除，正常返回每行的数据
            if (mColumnYu == 0) {
                positions = new int[mNumColumns];
                for (int i = 0; i < mNumColumns; i++) {
                    int posi = getHeaderViewsCount() > 0 ?
                            (position * mNumColumns + i - (mNumColumns * getHeaderViewsCount()))
                            : (position * mNumColumns + i);
                    T model = mData.get(posi);
                    models.add(model);
                    positions[i] = posi;
                }
            } else {
                // 不能整除时，判断
                // 是否是最后一行，是，返回剩余的列的数据
                if (position == mData.size() / mNumColumns + getHeaderViewsCount()) {
                    positions = new int[mColumnYu];
                    for (int i = 0; i < mColumnYu; i++) {
                        int posi = getHeaderViewsCount() > 0 ?
                                (position * mNumColumns + i - (mNumColumns * getHeaderViewsCount()))
                                : (position * mNumColumns + i);
                        T model = mData.get(posi);
                        models.add(model);
                        positions[i] = posi;
                    }
                } else {
                    // 否，正常返回每行的数据
                    positions = new int[mNumColumns];
                    for (int i = 0; i < mNumColumns; i++) {
                        int posi = getHeaderViewsCount() > 0 ?
                                (position * mNumColumns + i - (mNumColumns * getHeaderViewsCount()))
                                : (position * mNumColumns + i);
                        T model = mData.get(posi);
                        models.add(model);
                        positions[i] = posi;
                    }
                }
            }
            convert(viewHolder, positions, models);
            return viewHolder.getConvertView();
        }
    }

    /**
     * convert
     *
     * @param holder holder
     * @param positions positions
     * @param models models
     */
    public abstract void convert(GridViewHolder holder, int[] positions,
                                 List<T> models);

    public void addHeaderView(Component header) {
        if (header == null) {
            throw new IllegalArgumentException("headerView is null");
        }
        mHeaderViews.add(header);
        notifyDataChanged();
    }

    public void addFooterView(Component footer) {
        if (footer == null) {
            throw new IllegalArgumentException("footerView is null");
        }
        mFooterViews.add(footer);
        notifyDataChanged();
    }

    public Component getFooterView() {
        return mFooterViews.get(0);
    }

    public Component getHeaderView() {
        return mHeaderViews.get(0);
    }

    public void setEmptyView(Component component) {
        if (component != null) {
            this.mEmptyView = component;
            notifyDataChanged();
        }
    }

    public Component getEmptyView() {
        return mEmptyView;
    }

    public void removeHeaderView() {
        mHeaderViews.clear();
        notifyDataChanged();
    }

    public void removeFooterView() {
        mFooterViews.clear();
        notifyDataChanged();
    }

    public int getHeaderViewsCount() {
        return mHeaderViews.size();
    }

    public int getFooterViewsCount() {
        return mFooterViews.size();
    }

    public void setNumColumns(int numColumns) {
        this.mNumColumns = numColumns;
    }

    public static class GridViewHolder {
        // layout文件中的控件集合
        private final PlainArray<Component> mViews;
        private final Component             mConvertView;

        private GridViewHolder(Context context, ComponentContainer container, int layoutId) {
            this.mViews = new PlainArray<Component>();
            this.mConvertView = LayoutScatter.getInstance(context)
                    .parse(layoutId, container, false);
            this.mConvertView.setTag(this);
        }

        public static GridViewHolder get(Context context, Component convertView,
                                         ComponentContainer container, int layoutId, boolean isAdd) {
            GridViewHolder holder = null;
            if ((convertView == null) || (convertView.getTag() == null) || isAdd) {
                holder = new GridViewHolder(context, container, layoutId);
            } else {
                Object tag = convertView.getTag();
                if (tag instanceof GridViewHolder) {
                    holder = (GridViewHolder) tag;
                }
            }
            return holder;
        }

        /*
         * 根据ViewID获取控件对象，先从mViews集合中查找， 如果存在则直接返回该对象；
         * 不存在则从布局文件中获取该对象，然后添加到mViews集合中，然后再返回该对象；
         * */
        @SuppressWarnings("unchecked")
        private  <T extends Component> T getView(int viewId) {
            Component view;
            if (mViews.isEmpty() || !mViews.contains(viewId)) {
                view = mConvertView.findComponentById(viewId);
                mViews.put(viewId, view);
            } else {
                view = mViews.get(viewId).get();
            }
            return (T) view;
        }

        private Component getConvertView() {
            return mConvertView;
        }

        /**
         * 获取TextView控件
         *
         * @param viewId viewId
         * @return Text
         */
        public Text getTextView(int viewId) {
            Text text = null;
            Component view = getView(viewId);
            if (view instanceof Text) {
                text = (Text) view;
            }
            return text;
        }

        /**
         * 获取DirectionalLayout控件
         *
         * @param viewId viewId
         * @return DirectionalLayout
         */
        public DirectionalLayout getLinearLayout(int viewId) {
            DirectionalLayout layout = null;
            Component view = getView(viewId);
            if (view instanceof DirectionalLayout) {
                layout = (DirectionalLayout) view;
            }
            return layout;
        }
    }
}
